// utils/request.ts
import Taro from '@tarojs/taro';
import { WxLogin } from './wxLogin';

interface RequestOptions extends Omit<Taro.request.Option, 'url' | 'method'> {
  url: string;
  method?: keyof Taro.request.Option; // GET/POST/PUT/DELETE/PATCH
  needAuth?: boolean; // 是否需要 token
}

// 状态码映射
const ErrorCodeMap: Record<number, string> = {
  0: '成功',
  400: '请求参数不正确',
  401: '账号未登录',
  403: '没有该操作权限',
  404: '请求未找到',
  405: '请求方法不正确',
  423: '请求失败，请稍后重试',
  429: '请求过于频繁，请稍后重试',
  500: '系统异常',
  501: '功能未实现/未开启',
  502: '错误的配置项',
  900: '重复请求，请稍后重试',
  999: '未知错误',
};

class Request {
  private baseURL: string;
  private isRefreshing: boolean = false;
  private refreshQueue: Array<() => void> = [];

  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }

  private async getToken() {
    return Taro.getStorageSync('token') || '';
  }

  private async setToken(token: string) {
    Taro.setStorageSync('token', token);
  }

  private async handleRequest(options: RequestOptions): Promise<any> {
    const token = await this.getToken();
    const headers = options.needAuth
      ? { ...options.header, Authorization: `Bearer ${token}` }
      : options.header;

    // 分离 Taro.request 支持的字段，避免类型报错
    const { url, method = 'GET', data, header, ...rest } = options;

    try {
      const res = await Taro.request({
        url: this.baseURL + url,
        method,
        data,
        header: headers,
        ...rest,
      });

      const code = res.data?.code ?? res.statusCode;
      const msg = res.data?.message || ErrorCodeMap[code] || '未知错误';

      // token过期
      if (code === 401 && options.needAuth) {
        return this.handle401(options);
      }

      // 非正常状态统一提示
      if (code !== 0) {
        Taro.showToast({ title: msg, icon: 'none' });
        return Promise.reject({ code, msg, data: res.data });
      }

      return res.data?.data ?? res.data; // 返回 data 或整个响应
    } catch (error) {
      Taro.showToast({ title: '网络异常', icon: 'none' });
      return Promise.reject(error);
    }
  }

  private async handle401(options: RequestOptions) {
    if (this.isRefreshing) {
      return new Promise((resolve, reject) => {
        this.refreshQueue.push(() => {
          this.handleRequest(options).then(resolve).catch(reject);
        });
      });
    }

    this.isRefreshing = true;

    try {
      const newToken = await WxLogin.refreshToken();
      await this.setToken(newToken);

      // 刷新完成，执行队列
      this.refreshQueue.forEach(cb => cb());
      this.refreshQueue = [];
      return this.handleRequest(options);
    } catch (error) {
      // 刷新失败，跳转登录
      Taro.navigateTo({ url: '/pages/login/index' });
      return Promise.reject(error);
    } finally {
      this.isRefreshing = false;
    }
  }

  // 支持 GET/POST/PUT/DELETE/PATCH
  public get(url: string, params?: any, needAuth: boolean = true) {
    return this.handleRequest({ url, method: 'GET', data: params, needAuth });
  }

  public post(url: string, data?: any, needAuth: boolean = true) {
    return this.handleRequest({ url, method: 'POST', data, needAuth });
  }

  public put(url: string, data?: any, needAuth: boolean = true) {
    return this.handleRequest({ url, method: 'PUT', data, needAuth });
  }

  public delete(url: string, data?: any, needAuth: boolean = true) {
    return this.handleRequest({ url, method: 'DELETE', data, needAuth });
  }

  public patch(url: string, data?: any, needAuth: boolean = true) {
    return this.handleRequest({ url, method: 'PATCH', data, needAuth });
  }
  public async upload(
    url: string,
    filePath: string,
    name: string = 'file',
    needAuth: boolean = true
  ): Promise<any> {
    const token = needAuth ? (await this.getToken()) : '';
    try {
      const res = await Taro.uploadFile({
        url: this.baseURL + url,
        filePath,
        name,
        header: needAuth ? { Authorization: `Bearer ${token}` } : {},
      });
      const data = JSON.parse(res.data);
      if (data.code && data.code !== 0) {
        Taro.showToast({ title: data.message || '上传失败', icon: 'none' });
        return Promise.reject(data);
      }
      return data.data ?? data; // 返回上传后的文件信息
    } catch (err) {
      Taro.showToast({ title: '上传失败', icon: 'none' });
      return Promise.reject(err);
    }
  }}

export const request = new Request('http://gj-api.iin.cc/app-api');
